# -*- coding: UTF-8 -*-
# This is a sample commands.py.  You can add your own commands here.
#
# Please refer to commands_full.py for all the default commands and a complete
# documentation.  Do NOT add them all here, or you may end up with defunct
# commands when upgrading ranger.

# A simple command for demonstration purposes follows.
# -----------------------------------------------------------------------------

from __future__ import (absolute_import, division, print_function)

# You can import any python module as needed.
#import os

# You always need to import ranger.api.commands here to get the Command class:
from ranger.api.commands import Command

# git 命令插件
#import subprocess
#from ranger.api.commands import Command

# fzf-marks
#from __future__ import absolute_import, division, print_function
#import os
#from ranger.api.commands import Command

# 压缩与解压
#import os
from ranger.api.commands import *
from ranger.core.loader import CommandLoader

# Any class that is a subclass of "Command" will be integrated into ranger as a
# command.  Try typing ":my_edit<ENTER>" in ranger!
class my_edit(Command):
    # The so-called doc-string of the class will be visible in the built-in
    # help that is accessible by typing "?c" inside ranger.
    """:my_edit <filename>

    A sample command for demonstration purposes that opens a file in an editor.
    """

    # The execute method is called when you run this command in ranger.
    def execute(self):
        # self.arg(1) is the first (space-separated) argument to the function.
        # This way you can write ":my_edit somefilename<ENTER>".
        if self.arg(1):
            # self.rest(1) contains self.arg(1) and everything that follows
            target_filename = self.rest(1)
        else:
            # self.fm is a ranger.core.filemanager.FileManager object and gives
            # you access to internals of ranger.
            # self.fm.thisfile is a ranger.container.file.File object and is a
            # reference to the currently selected file.
            target_filename = self.fm.thisfile.path

        # This is a generic function to print text in ranger.
        self.fm.notify("Let's edit the file " + target_filename + "!")

        # Using bad=True in fm.notify allows you to print error messages:
        if not os.path.exists(target_filename):
            self.fm.notify("The given file does not exist!", bad=True)
            return

        # This executes a function from ranger.core.acitons, a module with a
        # variety of subroutines that can help you construct commands.
        # Check out the source, or run "pydoc ranger.core.actions" for a list.
        self.fm.edit_file(target_filename)

    # The tab method is called when you press tab, and should return a list of
    # suggestions that the user will tab through.
    # tabnum is 1 for <TAB> and -1 for <S-TAB> by default
    def tab(self, tabnum):
        # This is a generic tab-completion function that iterates through the
        # content of the current directory.
        return self._tab_directory_content()

# 自定义命令配置 Custom commands
# --------------------------------

# 压缩与解压
#import os
#from ranger.api.commands import *
#from ranger.core.loader import CommandLoader

class extract(Command):
    def execute(self):
        """Extract copied files to current directory or directory
        specified in a command line
        """

        cwd = self.fm.thisdir
        copied_files = cwd.get_selection()

        if not copied_files:
            return

        def refresh(_):
            cwd = self.fm.get_directory(original_path)
            cwd.load_content()

        one_file = copied_files[0]
        cwd = self.fm.thisdir
        original_path = cwd.path

        line_args = self.line.split()[1:]
        if line_args:
            extraction_dir = os.path.join(cwd.path, "".join(line_args))
            os.makedirs(extraction_dir, exist_ok=True)
            flags = ['-X', extraction_dir]
            flags += ['-e']
        else:
            flags = ['-X', cwd.path]
            flags += ['-e']

        self.fm.copy_buffer.clear()
        self.fm.cut_buffer = False

        if len(copied_files) == 1:
            descr = "Extracting: " + os.path.basename(one_file.path)
        else:
            descr = "Extracting files from: " + os.path.basename(one_file.dirname)
        obj = CommandLoader(args=['aunpack'] + flags \
            + [f.path for f in copied_files], descr=descr, read=True)

        obj.signal_bind('after', refresh)
        self.fm.loader.add(obj)

class extract_to_dirs(Command):
    def execute(self):
        """ Extract copied files to a subdirectories """

        cwd = self.fm.thisdir
        original_path = cwd.path
        copied_files = cwd.get_selection()

        if not copied_files:
            return

        def refresh(_):
            cwd = self.fm.get_directory(original_path)
            cwd.load_content()

        def make_flags(fn):
            fn_wo_ext = os.path.basename(os.path.splitext(fn)[0])
            flags = ['-X', fn_wo_ext]
            return flags

        one_file = copied_files[0]
        self.fm.copy_buffer.clear()
        self.fm.cut_buffer = False

        # Making description line
        if len(copied_files) == 1:
            descr = "Extracting: " + os.path.basename(one_file.path)
        else:
            descr = "Extracting files from: " + os.path.basename(one_file.dirname)

        # Extracting files
        for f in copied_files:
            obj = CommandLoader(
                args=['aunpack'] + make_flags(f.path) + [f.path],
                descr=descr, read=True
            )
            obj.signal_bind('after', refresh)
            self.fm.loader.add(obj)

# 解压文件
class extract7zziptar(Command):
    """:extract <paths>
    Extract archives
    """
    def execute(self):
        import os
        fail=[]
        for i in self.fm.thistab.get_selection():
            ExtractProg='7z x'
            if i.path.endswith('.zip'):
                # zip encoding issue
                ExtractProg='unzip -O gbk'
            elif i.path.endswith('.tar.gz'):
                ExtractProg='tar xvf'
            elif i.path.endswith('.tar.xz'):
                ExtractProg='tar xJvf'
            elif i.path.endswith('.tar.bz2'):
                ExtractProg='tar xjvf'
            if os.system('{0} "{1}"'.format(ExtractProg, i.path)):
                fail.append(i.path)
        if len(fail) > 0:
            self.fm.notify("Fail to extract: {0}".format(' '.join(fail)), duration=10, bad=True)
        self.fm.redraw_window()

## 压缩选中项
class zipcompress(Command):
    """:zipcompress
    """
    def execute(self):
        import os
        strcontent=[]
        CompressFileName=self.arg(1)
        self.fm.notify("[Debug] 输入参数 要压缩后的文件名{0}".format(CompressFileName))
        for i in self.fm.thistab.get_selection():
            self.fm.notify("[Debug] 选中的文件{0}".format(i.path))
            CompressProg='zip -ur'
            if os.system('{0} "{1}" "{2}"'.format(CompressProg, CompressFileName, i.path)):
                strcontent.append(i.path)
        if len(strcontent) > 0:
            self.fm.notify("压缩完成: {0}".format(' '.join(strcontent)))
        self.fm.redraw_window()

## 新建文件夹
class newfolder(Command):
    """:newfolder
    """
    def execute(self):
        import os
        fail=[]
        newFolderName=self.arg(1)
        self.fm.notify("[Debug] 要创建的 文件夹名 {0}".format(newFolderName))
        newFolderProg='mkdir -p'
        if os.system('{0} "{1}"'.format(newFolderProg, newFolderName)):
            fail.append(newFolderName)
        if len(fail) > 0:
            self.fm.notify("文件夹创建成功: {0}".format(' '.join(fail)))
        self.fm.redraw_window()

## 新建文件夹
class mkdir(Command):
    """:newfolder
    """
    def execute(self):
        import os
        fail=[]
        newFolderName=self.arg(1)
        self.fm.notify("[Debug] 要创建的 文件夹名 {0}".format(newFolderName))
        newFolderProg='mkdir -p'
        if os.system('{0} "{1}"'.format(newFolderProg, newFolderName)):
            fail.append(newFolderName)
        if len(fail) > 0:
            self.fm.notify("文件夹创建成功: {0}".format(' '.join(fail)))
        self.fm.redraw_window()

## 更新拉取 git 项目
class gpull(Command):
    """:gpull
    """
    def execute(self):
        import os
        self.fm.notify("[Debug] 更新拉取 git 项目")
        gitProg='git pull'
        retstr=os.system('{0}'.format(gitProg))
        self.fm.redraw_window()
        """
        if retstr== 0:
            print("return 0 represent success!")
            self.fm.notify("[Debug] 更新拉取 git 项目 成功")
        else:
            self.fm.notify("[Debug] {0}".format(retstr))
        """

## 使用fzf快速选择
class fzf_select(Command):
    """
    :fzf_select

    Find a file using fzf.

    With a prefix argument select only directories.

    See: https://github.com/junegunn/fzf
    """
    def execute(self):
        import subprocess
        import os.path
        if self.quantifier:
            # match only directories
            command = "find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
            -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf --preview '[ -d {} ] && ls -ahl {} || cat -n {}' +m"

        else:
            # match files and directories
            command = "find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
            -o -print 2> /dev/null | sed 1d | cut -b3- | fzf --preview '[ -d {} ] && ls -ahl {} || cat -n {}' +m"

        fzf = self.fm.execute_command(command,
                                      universal_newlines=True,
                                      stdout=subprocess.PIPE)
        stdout, stderr = fzf.communicate()
        #self.fm.notify("[Debug] stdout = {0} ".format(stderr))
        if fzf.returncode == 0:
            #self.fm.notify("[Debug] stdout = {0} ".format(stdout))
            fzf_file = os.path.abspath(stdout.rstrip('\n'))
            self.fm.notify("[Debug] stdout = {0} ".format(stdout))
            if os.path.isdir(fzf_file):
                self.fm.cd(fzf_file)
            else:
                self.fm.select_file(fzf_file)

class toggleVCS(Command):
    def execute(self):
        self.fm.execute_console("set vcs_aware!")
        self.fm.execute_console("reload_cwd")

## 使用rg搜索定位文件内容
class rg_find(Command):
    """
    :rg_find

    Find file content  using rg.

    With a prefix argument select only directories.

    See: https://hub.fastgit.org/BurntSushi/ripgrep
    See: https://hub.fastgit.org/phiresky/ripgrep-all
    """
    def execute(self):
        import subprocess
        import os.path
        import os
        if self.quantifier:
            # match only directories
            #command = "rg --column -w '' | fzf"
            #command ="rg --column -w '' | fzf --preview 'cat -n $( echo {} | cut -d : -f 1)'"
            #command ="rg --column -w '' | fzf --preview 'bat $( echo {} | cut -d : -f 1)  --color=always --wrap=auto --style=numbers --line-range=$( echo {} | cut -d : -f 2): '"
            #command ="rg --column -w '' | fzf --preview 'bat $( echo {} | cut -d : -f 1)  --color=always --wrap=auto --style=numbers --highlight-line=$( echo {} | cut -d : -f 2):$( echo {} | cut -d : -f 2) --line-range=$( echo {} | cut -d : -f 2):  '"
            #command ="rg --column -w '' | fzf --preview 'bat  $( echo {} | cut -d : -f 1)   --color=always  --wrap=auto  --style=numbers  --highlight-line=$(echo {} | cut -d : -f 2):$(echo {} | cut -d : -f 2)  --line-range=$(( $(echo {} | cut -d : -f 2)-10 )):  '"
            command ="rg --column '' | fzf --preview 'bat  $( echo {} | cut -d : -f 1)   --color=always  --wrap=auto  --style=numbers  --highlight-line=$(echo {} | cut -d : -f 2):$(echo {} | cut -d : -f 2)  --line-range=$(( $(( $(echo {} | cut -d : -f 2)-18 ))>18?$(( $(echo {} | cut -d : -f 2)-18 )):1 )):$(( $(echo {} | cut -d : -f 2)+18 ))  '"
        else:
            # match files and directories
            #command = "rg --column -w '' | fzf"
            #command ="rg --column -w '' | fzf --preview 'cat -n $( echo {} | cut -d : -f 1)'"
            #command ="rg --column -w '' | fzf --preview 'bat $( echo {} | cut -d : -f 1)  --color=always --wrap=auto --style=numbers --line-range=$( echo {} | cut -d : -f 2): '"
            #command ="rg --column -w '' | fzf --preview 'bat $( echo {} | cut -d : -f 1)  --color=always --wrap=auto --style=numbers --highlight-line=$( echo {} | cut -d : -f 2):$( echo {} | cut -d : -f 2) --line-range=$(echo {} | cut -d : -f 2):  '"
            #command ="rg --column -w '' | fzf --preview 'bat  $( echo {} | cut -d : -f 1)   --color=always  --wrap=auto  --style=numbers  --highlight-line=$(echo {} | cut -d : -f 2):$(echo {} | cut -d : -f 2)  --line-range=$(( $(echo {} | cut -d : -f 2)-10 )):  '"
            command ="rg --column '' | fzf --preview 'bat  $( echo {} | cut -d : -f 1)   --color=always  --wrap=auto  --style=numbers  --highlight-line=$(echo {} | cut -d : -f 2):$(echo {} | cut -d : -f 2)  --line-range=$(( $(( $(echo {} | cut -d : -f 2)-18 ))>18?$(( $(echo {} | cut -d : -f 2)-18 )):1 )):  '"
            # shell: 字符串运算   echo $((2-1 )) 
            #        三目运算：   $((2>1?2:1)) 
            #        command进行三目运算应该这样使用:   command1 && command2 || command3
        # 执行命令
        fzf = self.fm.execute_command(command,
                                      universal_newlines=True,
                                      stdout=subprocess.PIPE)
        stdout, stderr = fzf.communicate()
        #self.fm.notify("[Debug] fzf.returncode= {0} ".format(fzf.returncode))
        if fzf.returncode == 0:
            # 获得命令执行完的结果字符串
            #self.fm.notify("[Debug] stdout = {0} ".format(stdout))
            # 将字符串进行处理（拆分）
            varstring=stdout.split(':')
            fzf_file=varstring[0]
            #if os.path.isdir(fzf_file):
            #    self.fm.cd(fzf_file)
            #else:
            #    self.fm.select_file(fzf_file)
            # 使用vim打开并定位到第二行
            #self.fm.execute_command('vim ./README.md +2')
            self.fm.execute_command("vim  {0} +{1}".format(varstring[0],varstring[1]))


# git 命令插件
#import subprocess
#from ranger.api.commands import Command
class git(Command):

    commands = 'init status clone add rm restore commit remote push'.split()
    def execute(self):
        # empty
        if not self.arg(1):
            return self.fm.notify("For commands check \"git help\"")

        # help
        if self.arg(1) == "help":
            return self.fm.notify("Not done yet!", bad=True)

        # init
        if self.arg(1) == self.commands[0]:
            subprocess.run(["git", "init", "--quiet"])
            return self.fm.notify("Repository initialized successefully")

        # status
        if self.arg(1) == self.commands[1]:
            output = subprocess.check_output(["git", "status"]).decode()

            with open('/tmp/gitplug-status', 'w') as out:
                out.write(output)

            return self.fm.edit_file('/tmp/gitplug-status')

        # clone
        # TIP!
        #       to clone private repositorues you have to store your data
        #       using: "git config --global credential.helper store" in your
        #       terminals emulator(not ranger! it will not work!) and then
        #       do one pull still from terminals emulator and then you can
        #       clone private repositories from ranger.
        if self.arg(1) == self.commands[2]:
            if not self.arg(2):
                return self.fm.notify("Missing url!", bad=True)

            if self.arg(2):
                subprocess.run(["git", "clone", self.arg(2), "--quiet"])
                return self.fm.notify("Repository successfully cloned!")

        # add
        if self.arg(1) == self.commands[3]:
            if not self.arg(2):
                return self.fm.notify("Missing arguments! Usage :git add <file>", bad=True)

            if self.arg(2):
                subprocess.run(["git", "add", self.arg(2)])
                return self.fm.notify("Successfully added files to branch!")

        #rm
        if self.arg(1) == self.commands[4]:
            if not self.arg(2):
                return self.fm.notify("Missing arguments! Usage :git rm <file>", bad=True)

            if self.arg(2):
                subprocess.run(["git", "rm", self.arg(2)])
                return self.fm.notify("Successfully removed files from branch!")

        # restore
        if self.arg(1) == self.commands[5]:
            if not self.arg(2):
                return self.fm.notify("Missing arguments! Usage :git restore <file>", bad=True)

            if self.arg(2):
                subprocess.run(["git", "restore", "--staged", self.arg(2), "--quiet"])
                return self.fm.notify("Successfully restored files!")

        # commit
        if self.arg(1) == self.commands[6]:
            if not self.rest(2):
                return self.fm.notify("Missing commit text", bad=True)

            if self.rest(2):
                subprocess.run(["git", "commit", "-m", self.rest(2), "--quiet"])
                return self.fm.notify("Successfully commited!")
        
        # remote
        if self.arg(1) == self.commands[7]:
            if not self.arg(2):
                return self.fm.notify("Missing arguments! Use: git remote add/rm <name> <url>", bad=True)

            if self.arg(2) == "add":
                if not self.arg(3):
                    return self.fm.notify("Missing name and url!", bad=True)

                if self.arg(3):
                    if not self.arg(4):
                        return self.fm.notify("Missing url!", bad=True)

                    if self.arg(4):
                        subprocess.run(["git", "remote", "add", self.arg(3), self.arg(4)])
                        return self.fm.notify("Remote successfully added!")

            if self.arg(2) == "rm":
                if not self.arg(3):
                    return self.fm.notify("Missing name!", bad=True)

                if self.arg(3):
                    subprocess.run(["git", "remote", "rm", self.arg(3)])
                    return self.fm.notify("Remote successfully removed")

        # push
        if self.arg(1) == self.commands[8]:
            if self.arg(2) == "-u" and self.arg(3) and self.arg(4):
                subprocess.run(["git", "push", "--quiet", "-u", self.arg(3), self.arg(4)])
                return self.fm.notify("Repository successfully pushed")

            if not self.arg(2):
                subprocess.run(["git", "push", "--quiet"])
                return self.fm.notify("Repository successfully pushed")

#from __future__ import absolute_import, division, print_function
#import os
#from ranger.api.commands import Command
class fmark(Command):
    """
    :fmark
    Mark the current directory into fzf-marks file
    """

    def execute(self):
        if not self.arg(1):
            self.fm.notify(
                "A keyword must be given for the current bookmark!", bad=True
            )
            return

        mark_file = os.path.join(
            os.environ.get("HOME", os.path.expanduser("~")), ".fzf-marks"
        )
        mark_file = os.environ.get("FZF_MARKS_FILE", mark_file)
        item = "{} : {}".format(self.arg(1), self.fm.thisdir.path)

        if not os.path.exists(mark_file):
            with open(mark_file, "a") as f:
                pass

        with open(mark_file, "r") as f:
            for line in f.readlines():
                if line.split(":")[1].strip() == self.fm.thisdir.path:
                    self.fm.notify(
                        "Fzf bookmark already exists: {}".format(line.strip()), bad=True
                    )
                    return

        with open(mark_file, "a") as f:
            f.write("{}{}".format(item, os.linesep))
            self.fm.notify("Fzf bookmark has been added: {}".format(item))


class dmark(Command):
    """
    dmark: delete current directory from fzf-marks file
    """

    def execute(self):
        import subprocess

        mark_file = os.path.join(
            os.environ.get("HOME", os.path.expanduser("~")), ".fzf-marks"
        )
        mark_file = os.environ.get("FZF_MARKS_FILE", mark_file)
        items = None
        query = ""

        if self.arg(1):
            query = self.arg(1)

        if not os.path.exists(mark_file):
            self.fm.notify("No fzf bookmark is created yet!", bad=True)
            return

        # TODO: batch deletion
        command = '< "{}" sort -f | fzf --height 62% \
            -m --ansi --bind=ctrl-o:accept,ctrl-t:toggle --query="{}"'.format(
            mark_file, query
        )

        process = self.fm.execute_command(
            command, universal_newlines=True, stdout=subprocess.PIPE
        )
        stdout, stderr = process.communicate()
        if process.returncode == 0:
            items = stdout.rstrip().split("\n")

        if not items:
            return

        with open(mark_file, "r") as f:
            lines = f.readlines()

        with open(mark_file, "w") as f:
            for line in lines:
                if line.strip() not in items:
                    f.write(line)

        self.fm.notify("Fzf bookmark is deleted: {}".format(", ".join(items)))


class fzm(Command):
    """
    fzm: select and jump to bookmark stored in fzf-marks
    """

    def execute(self):
        import subprocess

        mark_file = os.path.join(
            os.environ.get("HOME", os.path.expanduser("~")), ".fzf-marks"
        )
        mark_file = os.environ.get("FZF_MARKS_FILE", mark_file)
        target = None
        query = ""

        if self.arg(1):
            query = self.arg(1)

        if not os.path.exists(mark_file):
            self.fm.notify("No fzf bookmark is created yet!", bad=True)
            return

        command = '< "{}" sort -f | fzf --height 62% \
            +m --ansi --bind=ctrl-o:accept,ctrl-t:toggle --query="{}" --select-1'.format(
            mark_file, query
        )

        process = self.fm.execute_command(
            command, universal_newlines=True, stdout=subprocess.PIPE
        )
        stdout, stderr = process.communicate()
        if process.returncode == 0:
            key, target = stdout.rstrip().split(" : ", 1)
            target = os.path.expanduser(target)

        if not target:
            return
        elif os.path.isdir(target):
            self.fm.cd(target)
        elif os.path.isfile(target):
            self.fm.select_file(target)
        else:
            self.fm.notify(
                "Unavailable fzf bookmark location: {} : {}".format(key, target), True
            )
